package com.yungu.swift.monitor.config;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.yungu.swift.monitor.model.Application;
import com.yungu.swift.monitor.model.Metric;
import com.yungu.swift.monitor.utils.SigarUtil;
import com.yungu.swift.utils.DateUtils;
import com.yungu.swift.utils.JsonUtils;
import com.yungu.swift.utils.ParamUtil;
import lombok.extern.slf4j.Slf4j;
import org.hyperic.sigar.CpuPerc;
import org.hyperic.sigar.Mem;
import org.hyperic.sigar.Sigar;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static com.yungu.swift.monitor.config.MonitorTask.QUEUE;

/**
 * @author : cuixiuyin
 * @date : 2019/10/12
 */
@Slf4j
@Configuration
@EnableScheduling
public class MonitorConfig {

    @Autowired
    private RestTemplate restTemplate;

    @Value("${server.port:3030}")
    private String port;

    private static final ExecutorService POOL = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors() + 1,
            15, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024),
            new ThreadFactoryBuilder().build(), new ThreadPoolExecutor.AbortPolicy());

    @Bean
    public MonitorTask monitorTask() {
        return new MonitorTask();
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    /**
     * 3分钟检查
     */
    @Scheduled(initialDelay = 10000L, fixedRate = 60000L * 3)
    public void monitor() {
        String url = "http://127.0.0.1:" + port + "/api/applications/";
        Map<String, Metric> map = new HashMap<>(16);
        // 1. 记录机器信息
        Application[] applications = restTemplate.getForObject(url, Application[].class);
        if (applications.length == 0) {
            return;
        }
        for (Application application : applications) {
            String id = application.getId();
            Metric metric = new Metric();
            // 应用状态、时间戳
            metric.setStatus(application.getStatusInfo().getStatus());
            metric.setTimestamp(application.getStatusInfo().getTimestamp());
            // 磁盘信息
            metric.setDisk(application.getStatusInfo().getDetails().getDiskSpace().getTotal());
            metric.setFreeDisk(application.getStatusInfo().getDetails().getDiskSpace().getFree());
            // 应用内存信息
            Map<String, Object> info = restTemplate.getForObject(url + id + "/metrics", Map.class);
            metric.setMemory((Integer) info.get("mem"));
            metric.setFreeMemory((Integer) info.get("mem.free"));
            metric.setHeapMemory((Integer) info.get("heap.init"));
            metric.setHeapUsedMemory((Integer) info.get("heap.used"));
            metric.setNonHeapMemory((Integer) info.get("nonheap.max"));
            metric.setNonHeapUsedMemory((Integer) info.get("nonheap.used"));
            // gc 信息
            metric.setYoungGcCount(ParamUtil.defaultIfNull((Integer) info.get("gc.g1_young_generation.count"), 0));
            metric.setYoungGcTime(ParamUtil.defaultIfNull((Integer) info.get("gc.g1_young_generation.time"), 0));
            metric.setOldGcCount(ParamUtil.defaultIfNull((Integer) info.get("gc.g1_old_generation.count"), 0));
            metric.setOldGcTime(ParamUtil.defaultIfNull((Integer) info.get("gc.g1_old_generation.time"), 0));
            // 线程信息
            metric.setCurrentThreads((Integer) info.get("threads"));
            try {
                // 机器内存信息
                Sigar sigar = SigarUtil.getSigar();
                Mem memory = sigar.getMem();
                metric.setMachineMemory(memory.getTotal());
                metric.setMachineFreeMemory(memory.getActualFree());
                // cpu信息
                List<CpuPerc> cpuPercs = Arrays.asList(sigar.getCpuPercList());
                double idle = cpuPercs.stream().mapToDouble(CpuPerc::getCombined).average().getAsDouble();
                metric.setIdle(idle);
            } catch (Exception e) {
                e.printStackTrace();
            }
            metric.setProcessors((Integer) info.get("processors"));

            map.put(application.getName(), metric);
        }
        log.info("{} 采集信息：{}", DateUtils.format(new Date()), JsonUtils.toJSONString(map));
        QUEUE.add(map);

        // 2. 交由线程池处理
        POOL.execute(monitorTask());
    }

}
